PyCon JP 2019: トークネタメモ
トークプロポーザル1
Title
Pythonの文字入出力のしくみ: print()がターミナルに文字を表示するまで
Pythonがprint()でターミナルに文字を表示するまで
Pythonがターミナルに文字を表示するまで
Pythonが画面に文字を表示するまで
Elevator Pitch
Pythonで画面へ文字表示したり、キーボードから入力するときには、OSの 標準入力, 標準出力 が使われています。 OSの標準入出力はPythonに限らず大多数のプログラムで使われていますが、これは何なのか仕組みを解説します。 また、Pythonで標準入出力がどう使われているのか、どんな使い方があるのか、使う時の注意点はなにかを紹介します。
Talk Format
45分
Audience Level
Beginner
Description
Pythonで画面へ文字表示するには、 print() 関数を使います。また、キーボードから入力するときには、 input() 関数を使います。このとき、プログラムの内部ではOSが提供する 標準入力, 標準出力 が使われています。この標準入出力は、Pythonに限らず大多数のプログラムで使われていますが、普段はあまり意識する事がありません。Pythonを書くときは、 print() は画面に文字を出力、 input() はキーボードから入力、くらいの理解で十分ということがほとんどです。 しかし、標準入出力がどんなものなのかの概念を理解出来れば、様々な応用が出来るようになります。例えば、画面に出力するかファイルに出力するかをコマンドラインオプションで切り替えるプログラムなどが簡単に作れるようになります。あるいは、なにかのトラブルに遭遇したときに何が起きているのか、すばやく原因を把握できるようになります。
このトークでは、低レイヤーの概念からPythonのレイヤーまでを通して、それぞれのレイヤーで入出力がどのように扱われているのか、各レイヤー間でどのようにつがっているのかを紹介します。
アジェンダ
1. Pythonの print(), input(), sys.stdout, sys.stdin の関係
2. 標準入出力とファイル入出力の違い
3. PythonとOSを繋ぐファイルディスクリプタ
4. 標準入力と標準出力とは何か: ターミナル、シェル、Python
5. トラブル: Pythonコマンドをリダイレクトしたら UnicodeEncodeError
6. Pythonで外部コマンドを起動して標準入出力を操作するsubprocessパッケージ
Notes
Tags
python, core, anythong else
Pythonに関連した原理・仕組み系トークの3回目
トークプロポーザル2
Title
ssh-agentに鍵を一括登録するsshhの開発とPython subprocessの使い方
Elevator Pitch
私は、ssh-agentに必要な分のssh秘密鍵を一括登録するツール sshh を開発しました。
このツールで使っているPythonのsubprocessパッケージは、Pythonから外部コマンドを起動して標準入出力を操作できます。
このトークでは、subprocessについて概要を紹介し、起動したプログラムの標準入出力とPythonをPIPEで繋ぎ対話的に入出力する方法を紹介します。そして、PIPEの出力を効率的にストリーミング処理する方法と、subprocessで扱えないssh-addの入出力を制御する方法を紹介します。
Talk Format
45分
Audience Level
Beginner
Description
私が開発したsshhは、ssh-agentに登録している鍵の数が一定数を超えると発生する ssh: Too many authentication failures (試行回数上限を超えると発生するエラー)を回避します。このエラーは、サーバー側で秘密鍵の試行回数上限を厳しく設定している場合に発生します。この問題は、ssh-agentに登録されている鍵を全てクリアして必要数分だけ登録するか、都度パスフレーズを入力することで回避できますが、複数の鍵とサーバーがある状況ではssh接続が非常に煩わしい作業になります。sshhは、Pythonのsubprocessパッケージを使って新しいssh-agentを起動し、さらにssh-addを呼びだして必要な分の秘密鍵を一括登録します。これで煩わしい作業から開放されます。 Pythonのsubprocessパッケージは、外部コマンドを起動するのに非常に便利なライブラリです。単に外部コマンドを起動するだけでなく、標準入出力をPIPEで繋ぐことで、外部コマンドのコンソール入出力を制御して処理を対話的に進められます。また、PIPEで受け取った外部コマンドの出力をストリーミング処理して、順次処理することもできます。ssh-addコマンドもsubprocessで呼びだしてパスフレーズをPIPEで渡すつもりでしたが、これは簡単ではありませんでした。
このトークでは、subprocessについて概要を紹介し、起動したプログラムの標準入出力とPythonをPIPEで繋ぎ対話的に入出力する方法を紹介します。そして、PIPEの出力を効率的にストリーミング処理する方法と、subprocessで扱えないssh-addの入出力を制御する方法を紹介します。
アジェンダ
1. sshhの紹介
2. 外部コマンドを起動するsubprocessパッケージの紹介
3. os.systemやos.spawnとの違い
4. 外部コマンドの標準出力を効率的にストリーミング処理する
5. PIPEによる外部コマンドの対話的な制御
6. 標準入出力で扱えないsshコマンドのパスフレーズ入力の制御
Notes
Tags
python, core, anythong else
発表ネタ
OSの動作
標準入力としてキーボードデバイスが接続されている
標準出力としてコンソールデバイスが接続されている
標準エラー出力としてコンソールデバイスが接続されている
いくつかのプログラミングの入門書では、最初に紹介してるものもある
この3つは、シェルのリダイレクトやパイプで接続を切り替えられる
ファイルディスクリプタ
ファイルデスクリプタは0,1,2は予約されている
0=標準入力(stdin)
1=標準出力(stdout)
2=標準エラー出力(stderr)
プログラムの起動時には、0,1,2はopenされている
他のファイルデスクリプタはopenするまで存在しない
実際のファイルとファイルデスクリプタ番号のペアはOSが管理している
ファイルデスクリプタ番号はプログラム毎に独立している
ファイルを開いてファイルデスクリプタ番号が10だったとして、これを他のプログラムに渡してもファイルにはアクセスできない(方法はある)
Pythonの動作
print関数はデフォルトでsys.stdoutに出力される。これは別のfile like objectに変更できる
input関数はsys.stdinから入力される。これは変更できない(たぶん)
sys.stdin, sys.stdout, sys.stderr を別の入出力に差し替えることで入出力先を変更できる
Pythonのローレイヤーの動作
ファイルデスクリプタは0,1,2は、Python起動時にはopenされている
実行例:
code:python
>> import os
>> os.write(1, b'hello\n')
hello
6
os.open, os.close, os.read, os.write はファイルデスクリプタを扱う低レイヤー関数
open で開いたファイルオブジェクトにもファイルディスクリプタ
code:python
>> f = open('a.txt', 'w')
>> f.fileno()
3
>> f2 = open('b.txt', 'w')
>> f2.fileno()
4
>> f.close()
>> f3 = open('c.txt', 'w')
>> f3.fileno()
3
>> f3 = open('d.txt', 'w')
>> f3.fileno()
5
コンソールのエンコーディングとリダイレクト
sys.stdout への書き込みは locale.getpreferredencoding() でエンコードされてコンソールに出力される
code:shell
$ python run.py > out.txt
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)
標準出力と、標準エラー出力
python run.py > output.txt でファイルに保存されない
subprocess.run
Pythonから別のプログラム起動してその出力を元に自動的に入力するプログラムを書けるようになります。
subprocess.PIPE
subprocess.Popen
ネタ
実装したいアイディアを実現するのに必要なのはPython以外の知識
askpassのパスワード入力を自動化するには?
標準入出力ではないプログラムに文字を渡すには?
pythonから起動元shellの環境変数を変更するには?
パスフレーズを暗号化して安全に保持するには?
プロセス起動とshellの機能
shell=False
ワイルドカード展開
リダイレクト
Popenはプロセス起動とshell起動を選べる
os.systemはshellに丸投げする
PS1 って知ってる?
export PS1=hogehoge
Pythonの sys.ps1, sys.ps2 も面白いよ